package liquidfs.core;

import java.io.*;

import com.thoughtworks.xstream.io.*;

import utility.*;

import liquidfs.*;
import liquidfs.data.ops.*;

public class Journal {

   private Application app;
   private RandomAccessFile file;
   private JournalEntry lastEntry = null;
   
   public Journal (Application app) {
      this.app = app;
      
      try {
         File filename = new File(app.getConfiguration().getJournalPath());
         filename.getParentFile().mkdirs();
         this.file = new RandomAccessFile (filename, "rwd");
      }
      catch (FileNotFoundException e) {
         throw new UserUncheckedException ("Could not create or read journal: " + e.getMessage(), e);
      }
   }
   
   public synchronized void update (JournalEntry entry) {
      this.lastEntry = entry;
      
      try {
         file.setLength(0);
         file.seek(0);
         file.write(entry.serialize(app));
      }
      catch (IOException e) {
         throw new UserUncheckedException ("Could not write to journal: " + e.getMessage(), e);
      }
   }
   
   public synchronized void startEntry (ModificationOperation<?> currentOperation, Object currentState) {
      this.update( new JournalEntry(currentOperation, currentState) );
   }
   
   public synchronized void startEntry (ModificationOperation<?> currentOperation, Object currentState, byte[] data) {
      this.update( new JournalEntry(currentOperation, currentState, data) );
   }
   
   public synchronized void updateEntry (Object currentState) {
      
      if (lastEntry != null)
         throw new IllegalStateException ("A previous update to the journal is required to only update the state.");
      
      this.startEntry(lastEntry.getCurrentOperation(), currentState);
   }
   
   public synchronized void endEntry () {
      try {
         file.setLength(0);
         file.seek(0);
      }
      catch (IOException e) {
         throw new UserUncheckedException ("Could not write to journal: " + e.getMessage(), e);
      }
   }
   
   public JournalEntry read () throws IOException {
      byte[] buffer = new byte[ (int) file.length()];
      file.readFully(buffer);
      
      try {
         return JournalEntry.deserialize(app, buffer);
      }
      catch (StreamException e) {
         return null;
      }
   }
}
